package com.yboot.starter.shiro.config;

import com.yboot.starter.shiro.core.filter.JwtFilter;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.InvalidRequestFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.CollectionUtils;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 功能描述
 *
 * @author yangcs
 * @date 2025/8/18
 */
@Configuration
@EnableConfigurationProperties({RedisProperties.class, SecurityProperties.class})
@ComponentScan(ShiroConfig.PACKAGE_NAME)
public class ShiroConfig {

    public static final String PACKAGE_NAME = "com.yboot.starter.shiro";

    private final SecurityProperties securityProperties;
    private final RedisProperties redisProperties;
    private final List<Realm> realmList;

    public ShiroConfig(SecurityProperties securityProperties,
                       RedisProperties redisProperties,
                       List<Realm> realmList) {
        this.securityProperties = securityProperties;
        this.redisProperties = redisProperties;
        this.realmList = realmList;
    }

//    @Bean
//    public UserContext userContext() {
//        return new ShiroUserContext();
//    }

    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //配置多个Realm securityManager.setRealms(Arrays.asList(userPasswordRealm(), smsCodeRealm()));
        //多认证策略    ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
        //设置策略为"所有Realm都成功" authenticator.setAuthenticationStrategy(new AllSuccessfulStrategy());
        // AtLeastOneSuccessfulStrategy (默认): 只要有一个Realm认证成功，整体就成功。
        // AllSuccessfulStrategy: 所有Realm都必须认证成功。
        // FirstSuccessfulStrategy: 使用第一个成功认证的Realm的结果，后续不再尝试。
        securityManager.setAuthenticator(modularRealmAuthenticator());
        securityManager.setRealms(realmList);
        // 自定义session管理 使用redis
        securityManager.setSessionManager(sessionManager());
        // 自定义缓存实现 使用redis
        securityManager.setCacheManager(cacheManager());
        return securityManager;
    }

    private static ModularRealmAuthenticator modularRealmAuthenticator() {
        ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
        authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        return authenticator;
    }

    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        RedisSessionDAO redisSessionDao = new RedisSessionDAO();
        redisSessionDao.setRedisManager(redisManager());
        sessionManager.setSessionDAO(redisSessionDao);
        return sessionManager;
    }
    /**
     * cacheManager 缓存 redis实现 使用的是shiro-redis开源插件
     *
     * @return
     */
    @Bean("shiroRedisCacheManager")
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }
    /**
     * 配置shiro redisManager 使用的是shiro-redis开源插件
     *
     * @return
     */
    @Bean("shiroRedisManager")
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setDatabase(redisProperties.getDatabase());
        redisManager.setHost(redisProperties.getHost() + ":" + redisProperties.getPort());
        redisManager.setPassword(redisProperties.getPassword());
        return redisManager;
    }

    @Bean("invalidRequest")
    public InvalidRequestFilter invalidRequestFilter() {
        InvalidRequestFilter invalidRequestFilter = new InvalidRequestFilter();
        // 禁用中文字符不合法的检查
        invalidRequestFilter.setBlockNonAscii(false);
        return invalidRequestFilter;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 添加自己的过滤器并且取名为jwt
        Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
        filterMap.put("jwt", new JwtFilter());
        shiroFilterFactoryBean.setFilters(filterMap);

        Map<String, String> map = new HashMap<String, String>();
        //对所有用户认证
        map.put("/**", "jwt");

        if (securityProperties.getExcludes() != null) {
            for (String excludeUrl : securityProperties.getExcludes()) {
                map.putIfAbsent(excludeUrl, "anon");
            }
        }
        //认证过滤器：
        //anon：无需认证即可访问，游客身份。
        //authc：必须认证（登录）才能访问。
        //authcBasic：需要通过 httpBasic 认证。
        //user：不一定已通过认证，只要是曾经被 Shiro 记住过登录状态的用户就可以正常发起请求，比如 rememberMe。

        //登录
        shiroFilterFactoryBean.setLoginUrl("/login.html");
        //首页
        shiroFilterFactoryBean.setSuccessUrl("/index.html");
        shiroFilterFactoryBean.setUnauthorizedUrl("/401");
        //错误页面，认证不通过跳转
        shiroFilterFactoryBean.setUnauthorizedUrl("/error");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

}
